// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of XPathFunctions
//
// XPathFunctions is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// XPathFunctions is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with XPathFunctions If not, see <http://www.gnu.org/licenses/>.
//	
//
//   $Id$// FuncLib.cpp : Implementation of CFuncLib
#include "stdafx.h"
#include <XPathFunctions.h>
#include "FuncLib.h"
#include <msxml2.h>
#include <ExecuteDispatch.h>
/////////////////////////////////////////////////////////////////////////////
// CFuncLib


STDMETHODIMP CFuncLib::serialise(IDispatch *pNodeset, BOOL bEscape, BOOL stripDisabled, BSTR *sSerialised)
{
	//serialises and optionally escapes content
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	CComBSTR sRetVal;

	CComPtr<IXMLDOMNodeList> pNL;
	HRESULT hr = pNodeset->QueryInterface(IID_IXMLDOMNodeList,(void**)&pNL);
	CComPtr<IXMLDOMNodeList> pNLToGo;
	GetNodesToSerialise(pNL,stripDisabled,&pNLToGo);
	if(SUCCEEDED(hr))
	{
		CComPtr<IXMLDOMNode> pNode;
		CComBSTR sNodeXML;
		while(SUCCEEDED(pNLToGo->nextNode(&pNode)) && pNode)
		{
			hr = pNode->get_xml(&sNodeXML);
			if(bEscape)
			{
				CStringW csNodeXML(sNodeXML);
				csNodeXML.Replace(L"<",L"&lt;");
				sRetVal.Append(csNodeXML);
			}
			else
			{
				sRetVal += sNodeXML;
			}
			pNode.Release();
		}
	}
	pNL.Release();
	*sSerialised = sRetVal.Detach();
	
	return hr;
}

STDMETHODIMP CFuncLib::HTMLserialise(IDispatch *pNodeset,BOOL stripDisabled, BSTR *sSerialised)
{
	//serialises, escapes and puts in div tags to force line breaks and indentation
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	CComBSTR sRetVal;
	
	//TODO:
	//This is currently being called recursaively, as a result, queryInterface
	//	is being uneccessarily called and checked.  Create an internal version
	//	of the method that takes a nodeset, and move all the code inside the 
	//	first if() into that.
	CComPtr<IXMLDOMNodeList> pNL;
	HRESULT hr = pNodeset->QueryInterface(IID_IXMLDOMNodeList,(void**)&pNL);
	CComPtr<IXMLDOMNodeList> pNLToGo;
	GetNodesToSerialise(pNL,stripDisabled,&pNLToGo);
	if(SUCCEEDED(hr))
	{
		CComPtr<IXMLDOMNode> pNode;
		CComBSTR sNodeXML;
		while(SUCCEEDED(pNLToGo->nextNode(&pNode)) && pNode)
		{
			DOMNodeType type;
			pNode->get_nodeType(&type);
			
			switch(type)
			{
				case NODE_ELEMENT :
				{
					//serialises using name, attributes, then children
					CComBSTR sName;
					pNode->get_nodeName(&sName);
					sNodeXML = CComBSTR("<div style='margin-left:1em;'>&lt;");
					sNodeXML += sName;
					
					//Get the Attributes as a string
					CComPtr<IXMLDOMNodeList> pNl;
					pNode->selectNodes(L"@*", &pNl);
					CComBSTR sNodeList;
					HTMLserialise(pNl,false,&sNodeList);
					//append the attribute string onto the node's 
					//	current XML then close the opening tag
					sNodeXML += sNodeList;
					sNodeXML += ">";
					//release the attr nodelist to reuse for the
					//	childNodes
					pNl.Release();
					//Now get the xml for the children
					pNode->get_childNodes(&pNl);
					HTMLserialise(pNl,false,&sNodeList);
					//append the string representaion of the children
					sNodeXML += sNodeList;
					//close the tag and the surrounding div.
					sNodeXML += "&lt;/";
					sNodeXML += sName;
					sNodeXML += "></div>";
					
					sRetVal += sNodeXML;
					break;
				}
				case NODE_DOCUMENT :
				{
					//this has no text representation at this level,
					//	but it does serialise its children
					CComPtr<IXMLDOMNodeList> pNl;
					pNode->get_childNodes(&pNl);
					HTMLserialise(pNl,false,&sNodeXML);
					pNl.Release();
					sRetVal += sNodeXML;
					break;
				}
				default:
				{
					//anything else serialises with the xml property
					CComBSTR sNodeXML;
					hr = pNode->get_xml(&sNodeXML);
					//prepend a space to prevent any gammy concatenation issues.
					sRetVal += " ";
					sRetVal += sNodeXML;
				}
				
			}
			pNode.Release();
			
		}

	}
	pNL.Release();
	*sSerialised = sRetVal.Detach();
	
	return hr;
}

STDMETHODIMP CFuncLib::addHelperLibrary(IDispatch *pHelper)
{
	m_pHelper = pHelper;
	if(m_pHelper)
		return S_OK;
	else
		return E_INVALIDARG;
}

HRESULT CFuncLib::GetNodesToSerialise(IXMLDOMNodeList* pNL,BOOL stripDisabled,IXMLDOMNodeList**pNLToGo)
{
	HRESULT hr = S_OK;
	if(stripDisabled && m_pHelper)
	{
		CComVariant vtResult;
		DISPID dispid = 0;
		CComVariant vtArg(pNL);
		DISPPARAMS params = {&vtArg,0,1,0};
		hr = ::InvokeDispatch(m_pHelper,L"StripDisabledNodes",&params,&vtResult,dispid,DISPATCH_METHOD,LOCALE_SYSTEM_DEFAULT, 0,0);
		if(SUCCEEDED(hr))
		{
			if(vtResult.vt == VT_UNKNOWN)
			{
				hr = vtResult.punkVal->QueryInterface(IID_IXMLDOMNodeList,reinterpret_cast<void**>(pNLToGo));
			}
			else if(vtResult.vt == VT_DISPATCH)
			{
				hr = vtResult.pdispVal->QueryInterface(IID_IXMLDOMNodeList,reinterpret_cast<void**>(pNLToGo));
			}

		}
		else
		{
			pNL->QueryInterface(IID_IXMLDOMNodeList,reinterpret_cast<void**>(pNLToGo));
		}

	}
	else
	{
		pNL->QueryInterface(IID_IXMLDOMNodeList,reinterpret_cast<void**>(pNLToGo));
	}
	return hr;
}